package com.quectel.pay.module.merchant.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.quectel.constant.global.SystemConstants;
import com.quectel.constant.pay.merchant.PayChannelConstants;
import com.quectel.pay.constants.RedisCacheConstants;
import com.quectel.pay.module.merchant.dao.PayChannelDao;
import com.quectel.pay.module.merchant.dto.PayChannelDto;
import com.quectel.pay.module.merchant.entity.PayChannelEntity;
import com.quectel.pay.module.merchant.service.PayChannelService;
import com.quectel.util.common.CopyUtils;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.kit.CacheKit;
import com.quectel.util.kit.Snowflake;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author ryan
 * @date 2021-01-27 14:26:22
 */
@DubboService
public class PayChannelServiceImpl extends ServiceImpl<PayChannelDao, PayChannelEntity> implements PayChannelService {

    private static final Logger LOGGER = LoggerFactory.getLogger(PayChannelServiceImpl.class);

    @Autowired
    private PayChannelDao payChannelDao;

    @Override
    public PayChannelDto selectByMchIdAndPayType(Long mchId, Byte payType) {

        return CacheKit.cacheToRedis(
                () -> {
                    PayChannelEntity entity = payChannelDao.selectOne(new LambdaQueryWrapper<PayChannelEntity>()
                            .eq(PayChannelEntity::getMchId, mchId)
                            .eq(PayChannelEntity::getPayType, payType)
                            .eq(PayChannelEntity::getStatus, PayChannelConstants.StatusEnum.NO_DEL.getStatus())
                            .last("limit 1"));
                    return CopyUtils.copyObj(entity, PayChannelDto.class);
                },
                RedisCacheConstants.PAY_CHANNEL_BY_MCH_AND_TYPE + mchId + "_" + payType,
                5 * 60
        );
    }

    @Override
    public PayChannelDto selectCacheById(Long id) {
        return CacheKit.cacheToRedis(
                () -> selectById(id),
                RedisCacheConstants.PAY_CHANNEL_BY_ID + id,
                5 * 60
        );
    }

    @Override
    public void deleteByMchIdAndType(Long mchId, Byte payType) {
        List<PayChannelEntity> payChannelEntities = payChannelDao.selectList(new LambdaQueryWrapper<PayChannelEntity>()
                .eq(PayChannelEntity::getMchId, mchId)
                .eq(PayChannelEntity::getPayType, payType)
                .eq(PayChannelEntity::getStatus, PayChannelConstants.StatusEnum.NO_DEL.getStatus()));
        for (PayChannelEntity v : payChannelEntities) {
            v.setStatus(PayChannelConstants.StatusEnum.IS_DEL.getStatus());
            payChannelDao.updateById(v);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdate(Long mchId, List<PayChannelDto> channelDtos) {
        if (CollectionUtils.isNotEmpty(channelDtos)) {
            for (PayChannelDto channelDto : channelDtos) {

                if (channelDto.getPayConfig() != null) {
                    // 把历史的可以匹配的商户进行配置更新同步 防止退款失败
                    List<PayChannelEntity> historyChannelList = payChannelDao.selectList(new LambdaQueryWrapper<PayChannelEntity>()
                            .eq(PayChannelEntity::getMchId, mchId)
                            .eq(PayChannelEntity::getPayType, channelDto.getPayType()));
                    // 先假删除
                    historyChannelList.stream().map(PayChannelEntity::getId).forEach(this::deleteById);

                    // 匹配历史以修改 比如同一个商户001 把密钥从123456改为654321 那么新增一条的同时需要把以前的123456也更改为654321以防止退款报错
                    List<PayChannelEntity> modifiedList = historyChannelList.stream().filter(v -> v.getPayConfig() != null).filter(
                            v -> {
                                if (SystemConstants.PayConfigType.ZHB.getType().equals(v.getPayType())) {
                                    PayChannelDto.AliChannelConfig receiveNewConfig = JacksonUtils.parse(channelDto.getPayConfig(), PayChannelDto.AliChannelConfig.class);
                                    PayChannelDto.AliChannelConfig config = JacksonUtils.parse(v.getPayConfig(), PayChannelDto.AliChannelConfig.class);
                                    return Objects.equals(receiveNewConfig.getAppId(), config.getAppId());
                                } else if (SystemConstants.PayConfigType.WX_JSAPI.getType().equals(v.getPayType())) {
                                    PayChannelDto.WeChatJSAPIChannelConfig receiveNewConfig = JacksonUtils.parse(channelDto.getPayConfig(), PayChannelDto.WeChatJSAPIChannelConfig.class);
                                    PayChannelDto.WeChatJSAPIChannelConfig config = JacksonUtils.parse(v.getPayConfig(), PayChannelDto.WeChatJSAPIChannelConfig.class);
                                    return Objects.equals(receiveNewConfig.getMchId(), config.getMchId())
                                            && Objects.equals(receiveNewConfig.getVersion(), config.getVersion());
                                } else if (SystemConstants.PayConfigType.WX_APP.getType().equals(v.getPayType())) {
                                    PayChannelDto.WeChatAppChannelConfig receiveNewConfig = JacksonUtils.parse(channelDto.getPayConfig(), PayChannelDto.WeChatAppChannelConfig.class);
                                    PayChannelDto.WeChatAppChannelConfig config = JacksonUtils.parse(v.getPayConfig(), PayChannelDto.WeChatAppChannelConfig.class);
                                    return Objects.equals(receiveNewConfig.getMchId(), config.getMchId())
                                            && Objects.equals(receiveNewConfig.getVersion(), config.getVersion());
                                }
                                return false;
                            }
                    ).map(v -> {
                        v.setPayConfig(channelDto.getPayConfig());
                        return v;
                    }).collect(Collectors.toList());
                    for (PayChannelEntity payChannelEntity : modifiedList) {
                        payChannelDao.updateById(payChannelEntity);
                    }


                    //新增最后一条
                    channelDto.setMchId(mchId);
                    channelDto.setStatus(PayChannelConstants.StatusEnum.NO_DEL.getStatus());
                    save(channelDto);
                }
            }
        }

    }


    /**
     * 默认脱敏
     *
     * @param mchId 商户id
     * @param hide  渠道信息是否脱敏
     * @return
     */
    @Override
    public List<PayChannelDto> queryListByMchId(Long mchId, Boolean hide) {
        if (Objects.isNull(hide)) {
            hide = true;
        }
        List<PayChannelEntity> payChannelEntities = payChannelDao.selectList(new LambdaQueryWrapper<PayChannelEntity>()
                .eq(PayChannelEntity::getMchId, mchId)
                .eq(PayChannelEntity::getStatus, PayChannelConstants.StatusEnum.NO_DEL.getStatus()));

        List<PayChannelDto> payChannelDtos = CopyUtils.copyList(payChannelEntities, PayChannelDto.class);
        if (Boolean.TRUE.equals(hide)) {
            for (PayChannelDto payChannelDto : payChannelDtos) {
                if (Objects.equals(payChannelDto.getPayType(), SystemConstants.PayConfigType.ZHB.getType())) {
                    //支付宝
                    PayChannelDto.AliChannelConfig configParams = JacksonUtils.parse(payChannelDto.getPayConfig(), PayChannelDto.AliChannelConfig.class);
                    configParams.setPrivateKey("****************");
                    configParams.setPublicKey("****************");
                    payChannelDto.setPayConfig(JacksonUtils.toJsonString(configParams));
                } else if (Objects.equals(payChannelDto.getPayType(), SystemConstants.PayConfigType.WX_JSAPI.getType())) {
                    PayChannelDto.WeChatJSAPIChannelConfig configParams = JacksonUtils.parse(payChannelDto.getPayConfig(), PayChannelDto.WeChatJSAPIChannelConfig.class);
                    configParams.setSecret("****************");
                    configParams.setKey("****************");
                    payChannelDto.setPayConfig(JacksonUtils.toJsonString(configParams));
                } else if (Objects.equals(payChannelDto.getPayType(), SystemConstants.PayConfigType.WX_APP.getType())) {
                    PayChannelDto.WeChatAppChannelConfig configParams = JacksonUtils.parse(payChannelDto.getPayConfig(), PayChannelDto.WeChatAppChannelConfig.class);
                    configParams.setSecret("****************");
                    configParams.setKey("****************");
                    payChannelDto.setPayConfig(JacksonUtils.toJsonString(configParams));
                }

            }
        }
        return payChannelDtos;
    }

    @Override
    public List<PayChannelDto> queryList(Map<String, Object> params) {
        List<PayChannelEntity> list = payChannelDao.queryList(params);
        return CopyUtils.copyList(list, PayChannelDto.class);
    }

    @Override
    public int queryTotal(Map<String, Object> params) {
        return payChannelDao.queryTotal(params);
    }

    @Override
    public Long save(PayChannelDto dto) {
        PayChannelEntity entity = CopyUtils.copyObj(dto, PayChannelEntity.class);
        entity.setId(Snowflake.nextId());
        payChannelDao.insert(entity);
        return entity.getId();
    }

    @Override
    public void updateById(PayChannelDto dto) {
        PayChannelEntity entity = CopyUtils.copyObj(dto, PayChannelEntity.class);
        payChannelDao.updateById(entity);
    }


    @Override
    public PayChannelDto selectById(Long id) {
        PayChannelEntity entity = payChannelDao.selectById(id);
        return CopyUtils.copyObj(entity, PayChannelDto.class);
    }

    @Override
    public void deleteById(Long id) {
//        payChannelDao.deleteById(id);
        PayChannelDto newlyDto = new PayChannelDto();
        newlyDto.setId(id);
        newlyDto.setStatus(PayChannelConstants.StatusEnum.IS_DEL.getStatus());
        updateById(newlyDto);
    }

    @Override
    @Transactional
    public void deleteBatch(Long[] ids) {
        for (Long id : ids) {
            deleteById(id);
        }
    }
}
